LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;

-- Opcode + Operation +   Description
-- 0000    +     SLL   +     OUTPUT <= A << B
-- 0001    +     SRL   +     OUTPUT <= A >> B
-- 0010    +     ADD   +     OUTPUT <= A + B
-- 0011    +     SUB   +     OUTPUT <= A - B
-- 0100    +     AND   +     OUTPUT <= A and B
-- 0101    +     NOR   +     OUTPUT <= A nor B
-- 0110    +     OR    +     OUTPUT <= A or B
-- 0111    +     XOR   +     OUTPUT <= A xor B
-- 1000   +     LT    +     OUTPUT <= (A < B) ? 1 : 0
-- 1001   +     LTU   +     OUTPUT <= (A < B) ? 1 : 0
entity alu is
  port ( 
    opcode: IN STD_LOGIC_VECTOR (3 downto 0);
    A, B: IN STD_LOGIC_VECTOR (31 downto 0);
    output: OUT STD_LOGIC_VECTOR (31 downto 0);
    negative: OUT STD_LOGIC;
    overflow, zero: OUT STD_LOGIC);
end alu;

architecture struct of alu is
  component addr
    port (
      A, B: in std_logic_vector(31 downto 0);
      CARRYIN : in std_logic;
      SUM : out std_logic_vector(31 downto 0);
      OVERFLOW: out std_logic
    );
  end component;


--internal signals  
	signal addout		:	std_logic_vector(31 downto 0);
	signal subout,ltout,ltuout		:	std_logic_vector(31 downto 0);	
	signal sllout, slrout, shift16, shift8, shift4, shift2, shift1		:	std_logic_vector(31 downto 0);	
	signal notB    : std_logic_vector(31 downto 0);
	signal addoverflow, suboverflow : std_logic;
	signal s16, s8, s4, s2, s1 : std_logic;
	signal intoutput : std_logic_vector(31 downto 0);
	signal intopcode : std_logic_vector(3 downto 0);
		
begin

  intopcode <= opcode;
  
  notB <= B nand B;
  
  U01: addr   port map(A, B,    '0', addout, addoverflow);
  U02: addr   port map(A, notB, '1', subout, suboverflow);


  
  s16 <= '1' when (B(4) = '1') else '0';
  s8  <= '1' when (B(3) = '1') else '0';
  s4  <= '1' when (B(2) = '1') else '0';    
  s2  <= '1' when (B(1) = '1') else '0';
  s1  <= '1' when (B(0) = '1') else '0';
--Y1 <= ?1? when (A = ?0? or B = ?0?) and Sel = ?1? else ?0?;


  shift16 <= A(15 downto 0) & x"0000" when intopcode = "0000" and s16 = '1' else 
             x"0000" & A(31 downto 16)  when intopcode = "0001" and s16 = '1' else 
             A(31 downto 0);
             
  shift8 <= shift16(23 downto 0) & x"00" when intopcode = "0000" and s8 = '1' else 
             x"00" & shift16(31 downto 8)  when intopcode = "0001" and s8 = '1' else 
             shift16(31 downto 0);
             
  shift4 <= shift8(27 downto 0) & x"0" when intopcode = "0000" and s4 = '1' else 
             x"0" & shift8(31 downto 4)  when intopcode = "0001" and s4 = '1' else 
             shift8(31 downto 0);
             
  shift2 <= shift4(29 downto 0) & "00" when intopcode = "0000" and s2 = '1' else 
             "00" & shift4(31 downto 2)  when intopcode = "0001" and s2 = '1' else 
             shift4(31 downto 0);
             
  shift1 <= shift2(30 downto 0) & '0' when intopcode = "0000" and s1 = '1' else 
             '0' & shift2(31 downto 1)  when intopcode = "0001" and s1 = '1' else 
             shift2(31 downto 0);
                                                                 

  sllout <= shift1;
  slrout <= shift1;             
  
  ltout <= "0000000000000000000000000000000" & subout(31);

--A(31) xor B(31) => not ltout
  with (A(31) xor B(31)) select
    ltuout <= ltout when '0',
              "0000000000000000000000000000000" & not subout(31) when '1',
              x"BAD1BAD1" when others;
              
  --ltuout <= subout; --sign bits not equal, flip negative flag
  
  with intopcode select
    intoutput <= sllout  when "0000",
              slrout  when "0001",
              addout  when "0010",
              subout  when "0011",
              ltout   when "1000",
              ltuout  when "1001",
              A and B when "0100",
              A nor B when "0101",
              A or  B when "0110",
              A xor B when "0111",
            x"BAD1BAD1" when others; 
            
  with intopcode select
    overflow <= suboverflow  when "0011",
                addoverflow  when "0010",
                '0' when "0100" | "0101"| "0110" | "0111" | "0000" | "0001",
                '0' when others;
                 
  zero <= '1' when intoutput = x"00000000" else '0';
  negative <= '1' when intoutput(31) = '1' else '0';
  output <= intoutput;          
  
end struct;
